#ifndef _CPU_H_
#define _CPU_H_

#include <types.h>
#include <Paging.h>

#define CPUHasFeature(a)	(bool) CPU::Features.a
#define BIT(a)	dword a	:1

struct CPUFamilyStepping {
	dword stepping	:4;
	dword model	:4;
	dword family	:4;
	dword type	:2;
	dword extModel	:4;
	byte extFamily;
	dword reserved	:6;
} __attribute__ ((packed));

struct CPUBrandID {
	byte BrandID;
	byte CLFLUSH;
	byte CPUCount;
	byte APICID;
} __attribute__ ((packed));

struct CPUFeatures {
	dword FPU	:1;
	dword VME	:1;
	dword DE	:1;
	dword PSE	:1;
	dword TSC	:1;
	dword MSR	:1;
	dword PAE	:1;
	dword MCE	:1;
	
	dword CX8	:1;
	dword APIC	:1;
	dword reserved	:1;
	dword SEP	:1;
	dword MTRR	:1;
	dword PGE	:1;
	dword MCA	:1;
	dword CMOV	:1;
	
	dword PAT	:1;
	dword PSE36	:1;
	dword PSN	:1;
	dword CLFL	:1;
	dword reserved2	:1;
	dword DTES	:1;
	dword ACPI	:1;
	dword MMX	:1;
	
	dword FXSR	:1;
	dword SSE	:1;
	dword SSE2	:1;
	dword SS	:1;
	dword HTT	:1;
	dword TM1	:1;
	dword IA64	:1;
	dword PBE	:1;

	BIT(SSE3);
	BIT(PCLMUL);
	BIT(DTES64);
	BIT(MON);
	BIT(DSCPL);
	BIT(VMX);
	BIT(SMX);
	BIT(EST);
	
	BIT(TM2);
	BIT(SSSE3);
	BIT(CID);
	BIT(reserved3);
	BIT(FMA);
	BIT(CX16);
	BIT(ETPRD);
	BIT(PDCM);
	
	BIT(reserved4);
	BIT(reserved5);
	BIT(DCA);
	BIT(SSE41);
	BIT(SSE42);
	BIT(x2APIC);
	BIT(MOVBE);
	BIT(POPCNT);
	
	BIT(reserved6);
	BIT(AES);
	BIT(XSAVE);
	BIT(OSXSAVE);
	BIT(AVX);
	dword reserved7	:3;
} __attribute__ ((packed));

#undef BIT

class CPU {
public:
	static void Init(dword id);

	/** OMGosh... Hope this is not needed */
	static void Stop();
	static void EnableInt();
	static bool DisableInt();
	static void Halt();

	static dword ReadCR0();
	static dword ReadCR1();
	static dword ReadCR2();
	static dword ReadCR3();
	static dword ReadCR4();
	
	static void WriteCR0(dword);
	static void WriteCR1(dword);
	static void WriteCR2(dword);
	static void WriteCR3(dword);
	static void WriteCR4(dword);

	static void CPUID(dword, dword *, dword *, dword *, dword *);
	static void ReadInfo();
	
private:
	static char CPUBrand[13];
	static char Name[49];
	static struct CPUFamilyStepping FamilyStepping;
	static struct CPUBrandID BrandID;
	static struct CPUFeatures Features;
};

#endif
